超越“双十一” | ebay支付核心账务系统架构演进之路
(图源网络)
供稿 | Payments 贾奇
编辑 | 顾欣怡本文4289字,预计阅读时间14分钟更多干货请关注“eBay技术荟”公众号导读
Part 01
业务背景
在正式介绍架构前,先简要看下业务流程,示意图如下:
图 1(点击可查看大图)
上游客户端往账务系统发出转账请求,从A账号往B账号转100元; 系统收到请求后,查看当前两个账号的余额,分别为100元和50元;
A账号有足够的余额,转账开始,系统从A账号扣除100元,并在B账号加上100元。
数据高可靠:账户余额等信息是极为重要的核心业务数据,绝对不能丢。 服务高可用:作为一项互联网服务,交易每时每刻都在发生,秒级别的不可用也将损害用户体验。
处理高吞吐:面对ebay主站百万卖家时刻都在产生的支付流量,要求系统能在短时间内处理大量请求。
数据高安全:必须具有防盗防篡改的数据保护机制(本文暂不作介绍)。
Part 02
架构演进:从1.0到5.0
架构1.0
图 2(点击可查看大图)
图2是最简单的单进程单线程架构,所有账号的余额都在内存中。它有很多问题,其中最大问题是当进程退出后,余额信息就会永远丢失。图 4(点击可查看大图)
有经验的读者会想到把信息保存在数据库,数据库有主从备份,这样即使机器突然没了,换台机器连上数据库依然能提供服务(图4)。这里有个技术细节是,为了提高效率,一般备份是异步进行的,主备库之间的数据往往有延时,如果极端情况发生,主库突然挂了,主库中的最新数据在备库里是没有的,就可能发生多扣少加的情况。余额等信息是支付中极为重要的核心金融数据,绝不能丢。所以首先要解决的问题就是保证数据不丢失。架构2.0
图 5(点击可查看大图)
架构2.0和1.0最大的区别在于采用了Raft共识算法[1]来做数据的实时备份。该算法保证只要集群中的多数派存活,数据就不会丢失。根据对数据可靠性的不同要求,有相应的部署方案。一般生产环境下采用三地五中心的部署,这样即使一个数据中心因为天灾人祸挂了,我们依然可以在剩下的两个数据中心找到数据。该架构最大的问题在于FAS进程(图5中蓝色框)是单点,一旦挂了,服务对上游就完全不可用。对于日均千万转账流量来说,将极大影响用户体验,完全不可接受。有同学可能会想到引入一个管理器(Process Manager),和FAS进程保持心跳,如果一段时间内收不到心跳,管理器就自动创建一个新的FAS进程。这样做的问题有两个,一是管理器本身就是单点;二是心跳超时不足以说明进程已经不存在。而多个FAS进程同时处理上游请求的问题在于无法保证对同一账户转账的事务性。到这里,首要挑战变成了系统的可用性。架构3.0
同一时刻只能有一个leader; 当发生failover时,新leader的状态(账户余额)必须和上任leader下台前达到完全一致,才能开始处理新的请求。
图 6(点击可查看大图)
我们创造性地提出了“双主合一”的设计,将业务处理和数据同步放到同一个进程中,很好地解决了第一个问题。这是因为Raft leader所在的进程,也必定是业务处理的leader,而Raft算法保证了多数派leader的唯一性。即使因为脑裂出现两个leader的情况,由于少数派的leader无法向多数派同步数据,对外部系统来说也是绝对安全的。以下是线程模型:架构4.0
接下来要解决的问题是当发生换主,如何让新leader的状态恢复到和前任leader下台前一致。为此,我们引入了一个基于领域驱动模型设计(Domain-Driven-Design)[2]和事件溯源(Event Sourcing)[3]的新编程模型(图9)。
Process:状态机处理命令,产生事件,即本次转账A账号减了100元,B账号加了100元。这一步不更新状态。 Apply:根据事件更新状态。
公式 1
然而工程实现中我们并没有这么做,因为恢复时间(即服务不可用时间)也会相应变得很长:恢复时间和事件数量成线性正相关。假设Apply一个事件耗时10微秒(us),一天产生一千万个事件,Apply一天的数据也需要2分钟。 本地磁盘空间有限,只会保留最近几天的数据,其余数据会被远程备份到数据仓库,考虑到下载,实际所需时间会更长。
我们首先使用快照(Snapshot)来缩短时间。系统定期把当前状态做成快照写到本地磁盘,恢复时,先加载最近一次快照,接着Apply后面的事件。优化后状态恢复所需时间为:
公式 2
快照加载时间和文件大小有关,假设磁盘读文件的速度是500MB/秒,读10GB的快照文件只需20秒。Apply所需时间和快照时间到当前的事件个数有关,不再与历史总数据量相关。由于快照保存在本地磁盘,也省去了网络下载时间。假设一天打一次快照,状态恢复只需2分钟。为了进一步缩短时间,我们引入了EventApplyLoop(EAL)线程,只要有新的事件写入事件仓库,EAL就会立即调用Apply更新状态。由于近实时更新,当新leader上台后,需要Apply的事件数量非常有限,毫秒级别即可完成状态恢复,上游几乎无感知。架构5.0
架构4.0基本上能满足本文刚开始提到的所有需求,也确实在预生产环境中跑了相当长一段时间……直到我们在数据迁移过程中遇到了内存问题(图11)。
EAL所Apply的事件来自于事件仓库,产生的状态更新直接写入RocksDB[4]。 CPL所Apply的事件包含尚未成功提交到事件仓库的部分,产生的状态属于临时状态,不会写入RocksDB,而是留在内存中。一来可以提高查询速度,二来如果换主导致事件无法写入仓库,状态可以直接丢弃。
CPL在查询时,会先看内存中是否有相关数据,只有当不存在时才会查询RocksDB。
图 14(点击可查看大图)
从图14可以看出,跨度为两天的压力测试,经历了800多次换主。一方面,流量丝毫没有受到换主影响,始终保持高可用;另一方面,数十万账号之间上亿笔的转账,只要有任何一笔处理有误,就会波及剩余大部分账号。我们将测试结果和预期结果进行一一比对,不仅比对每个账号上的余额,还按序比对每个账号每一次转账的事件,结果全部匹配。Part 03
未来工作
经工具分析,系统能力还有30~50%的提升空间。未来将对系统进行全面深入的优化,包括进一步简化业务逻辑,处理过程串行改并行,用Reliable UDP代替TCP来传输数据等。不久的将来,ebay支付将服务全球所有卖家,层出不穷的衍生业务也将带来更多流量。由于单个系统处理能力有上限,为了应对不断增长的流量,FAS整体架构必须具有动态扩容能力。我们在去年的百万TPS实验上已经证明了这一点,接下来的工作就是要对它进行打磨改进,正式用于生产环境。(详见超越“双十一”—— ebay百万TPS支付账务系统的设计与实现)FAS产生的数据可以作为其他业务的信息源(Source of Truth),例如各类金融报表、查询服务、智能预测等。这类业务种类繁多,需求千差万别,但都是基于上游FAS数据。我们将提供一整套服务+框架的解决方案,以一种安全、准确、有序、高效、简单的方式让这些应用接收并处理数据。FAS是一个分层架构,应用层和Raft层解耦。对于同类应用,只需替换应用层的业务逻辑即可。未来将支持更多的主流编程语言,让用户能以更低的成本开发业务逻辑。
Part 04
结语
目前架构5.0已经在生产环境下稳定运行超过7个月。对上游,它就像单进程一样对外提供服务,却具有单进程所缺少的工业级的数据防丢失、服务高可用、高吞吐等特性。我们认为在不久的将来,在金融、医疗等领域,这类应用会成为一种新常态,我们也已对该项目开源[6](业务逻辑除外),希望可以为同类应用的开发带来帮助。
参考文献:[1]https://raft.github.io/
[2]https://martinfowler.com/bliki/DomainDrivenDesign.html[3]https://martinfowler.com/eaaDev/EventSourcing.html[4]https://rocksdb.org/[5]https://github.com/Netflix/chaosmonkey[6]https://github.com/eBay/Gringofts您可能还感兴趣:
超越“双十一”—— ebay百万TPS支付账务系统的设计与实现平台迁移那些事 | eBay GC调优策略的实践平台迁移那些事 | eBay百亿级流量迁移策略分享 | eBay TESS,我心中的那朵“云”前沿 | BERT在eBay推荐系统中的实践eBay云计算“网”事|网络重传篇eBay云计算“网”事|网络丢包篇eBay云计算“网”事 | 网络超时篇数据之道 | Akka Actor及其在商业智能数据服务中的应用数据之道 | 属性图在增强分析平台中的实践数据之道 | SLA/SLE监控与告警
数据之道 | 进阶版Spark执行计划图
eBay大量优质职位,等的就是你